package com.axinfu.util.validjsonq;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.axinfu.util.EmptyUtil;
import com.axinfu.util.validq.ValidQ;

/**
 * 核心的基于json规则验证的验证器装载器
 *
 * @author zjn
 * @since 2022/3/23
 */
@SuppressWarnings("all")
public class ValidJsonQCoreLoader implements ValidJsonQLoader {

    @Override
    public void loadObject(ValidQ validQ, String group, JSONObject config, JSONObject data, String parentField,
                           String parentFieldName, Integer level) {
        boolean ignoreGroup = EmptyUtil.isEmpty(group);
        parentField = EmptyUtil.isNotEmptyOrDefault(parentField, "");
        parentFieldName = EmptyUtil.isNotEmptyOrDefault(parentFieldName, "");
        level = EmptyUtil.isNotEmpty(level) ? level + 1 : 1;

        if (EmptyUtil.isEmpty(data)) {
            data = new JSONObject();
        }

        for (String key : config.keySet()) {
            JSONObject field = config.getJSONObject(key);
            //key对应的value为null或为空对象，跳过该字段
            if (EmptyUtil.isEmpty(field)) {
                continue;
            }

            /*
                基础属性
             */
            String attrField = EmptyUtil.isNotEmpty(parentField, parentField + ".", "") + key;
            String attrShortFieldName = field.getString("name");
            if (EmptyUtil.isEmpty(attrShortFieldName)) {
                attrShortFieldName = key;
            }
            String attrFieldName =
                    EmptyUtil.isNotEmpty(parentFieldName, parentFieldName + ".", "") + attrShortFieldName;

            /*
                解析验证器
             */
            JSONObject valid = field.getJSONObject("valid");
            //valid为null或为空对象，无需验证
            if (EmptyUtil.isNotEmpty(valid)) {
                for (String validKey : valid.keySet()) {
                    JSONObject validationConfig = valid.getJSONObject(validKey);
                    if (null != validationConfig && validationConfig.size() != 0) {
                        Boolean validatorEnable = validationConfig.getBoolean("enable");
                        if (EmptyUtil.isEmpty(validatorEnable)) {
                            validatorEnable = true;
                        }
                        String validatorGroup = validationConfig.getString("group");
                        if (null == validatorGroup || "".equals(validatorGroup.trim())) {
                            validatorGroup = group;
                        }
                        if (validatorEnable && (ignoreGroup || validatorGroup.equals(group))) {
                            Object target = data.get(key);
                            Integer validatorLevel = validationConfig.getInteger("level");
                            level = EmptyUtil.isEmpty(validatorLevel) ? level : validatorLevel;
                            Integer validatorIndex = validationConfig.getInteger("index");
                            ValidQElement element =
                                    ValidQMapper.INSTANCE.get(validKey);
                            element.getValidatorLoader().load(validQ, validationConfig, target, attrField,
                                    key, attrFieldName, attrShortFieldName, level, validatorIndex);
                        }
                    }
                }
            }

            /*
                处理下级验证
             */
            JSONObject childConfig = field.getJSONObject("childValid");
            if (EmptyUtil.isEmpty(childConfig)) {
                continue;
            }

            //字段类型
            String type = field.getString("type");
            loadChild(validQ, group, level, attrField, attrFieldName, childConfig, type,
                    data, null, key, null);
        }
    }

    @Override
    public void loadList(ValidQ validQ, String group, JSONObject config, JSONArray data, String parentField,
                         String parentFieldName, Integer level) {
        parentField = EmptyUtil.isNotEmptyOrDefault(parentField, "");
        parentFieldName = EmptyUtil.isNotEmptyOrDefault(parentFieldName, "");
        level = null == level ? 1 : level + 1;

        if (EmptyUtil.isEmpty(data)) {
            data = new JSONArray();
        }

        for (int i = 0, l = data.size(); i < l; i++) {
            JSONObject childData4List = data.getJSONObject(i);

            String attrField = String.format("%s[%s]", parentField, i);
            String attrFieldName;
            if (parentFieldName.equals(parentField)) {
                attrFieldName = attrField;
            } else {
                attrFieldName = String.format("%s[第%s条]", parentFieldName, i + 1);
            }

            loadObject(validQ, group, config, childData4List, attrField, attrFieldName, level);
        }
    }

    @Override
    public void loadArray(ValidQ validQ, String group, JSONObject config, JSONArray data, String parentField,
                          String parentFieldName, Integer level) {
        boolean ignoreGroup = null == group || "".equals(group.trim());
        parentField = EmptyUtil.isNotEmptyOrDefault(parentField, "");
        parentFieldName = EmptyUtil.isNotEmptyOrDefault(parentFieldName, "");
        level = EmptyUtil.isNotEmpty(level, level + 1, 1);

        if (EmptyUtil.isEmpty(data)) {
            data = new JSONArray();
        }

        for (int i = 0, l = data.size(); i < l; i++) {
            String attrShortField = String.format("[%s]", i);
            String attrField = parentField + attrShortField;
            String attrShortFieldName;
            if (parentFieldName.equals(parentField)) {
                attrShortFieldName = attrShortField;
            } else {
                attrShortFieldName = String.format("[第%s条]", i + 1);
            }
            String attrFieldName = parentFieldName + attrShortFieldName;

            /*
                解析验证器
             */
            JSONObject valid = config.getJSONObject("valid");
            //valid为null或为空对象，无需验证
            if (EmptyUtil.isNotEmpty(valid)) {
                for (String validKey : valid.keySet()) {
                    JSONObject validationConfig = valid.getJSONObject(validKey);
                    if (null != validationConfig && validationConfig.size() != 0) {
                        Boolean validatorEnable = validationConfig.getBoolean("enable");
                        if (null == validatorEnable) {
                            validatorEnable = true;
                        }
                        String validatorGroup = validationConfig.getString("group");
                        if (EmptyUtil.isEmpty(validatorGroup)) {
                            validatorGroup = group;
                        }
                        if (validatorEnable && (
                                ignoreGroup || validatorGroup.equals(group)
                        )) {
                            Object target = data.get(i);
                            Integer validatorLevel = validationConfig.getInteger("level");
                            level = EmptyUtil.isEmpty(validatorLevel) ? level : validatorLevel;
                            Integer validatorIndex = validationConfig.getInteger("index");
                            validatorIndex = null == validatorIndex ? 1 : validatorIndex;
                            ValidQElement element =
                                    ValidQMapper.INSTANCE.get(validKey);
                            element.getValidatorLoader().load(validQ, validationConfig, target, attrField,
                                    attrShortField, attrFieldName, attrShortFieldName, level, validatorIndex);
                        }
                    }
                }
            }

            /*
                处理下级验证
             */
            JSONObject childConfig = config.getJSONObject("childValid");
            if (EmptyUtil.isEmpty(childConfig)) {
                continue;
            }

            //字段类型
            String type = config.getString("type");
            loadChild(validQ, group, level, attrField, attrFieldName, childConfig, type,
                    null, data, null, i);
        }
    }

    /**
     * loadChild
     *
     * @param validQ        validQ
     * @param group         group
     * @param level         level
     * @param attrField     attrField
     * @param attrFieldName attrFieldName
     * @param childConfig   childConfig
     * @param type          type
     * @param data          data
     * @param dataArray     dataArray
     * @param key           key
     * @param i             i
     */
    private void loadChild(ValidQ validQ, String group, Integer level, String attrField, String attrFieldName,
                           JSONObject childConfig, String type, JSONObject data, JSONArray dataArray, String key,
                           Integer i) {
        if (EmptyUtil.isEmpty(type)) {
            type = DataType.STRING.getType();
        }
        DataType dataType = DataType.parse(type);
        if (EmptyUtil.isEmpty(dataType)) {
            dataType = DataType.STRING;
        }

        JSONObject jsonObject;
        JSONArray jsonArray;
        switch (dataType) {
            case OBJECT:
                if (EmptyUtil.isNotEmpty(data)) {
                    jsonObject = data.getJSONObject(key);
                } else {
                    jsonObject = dataArray.getJSONObject(i);
                }
                loadObject(validQ, group, childConfig, jsonObject, attrField, attrFieldName, level);
                break;
            case LIST:
                if (EmptyUtil.isNotEmpty(data)) {
                    jsonArray = data.getJSONArray(key);
                } else {
                    jsonArray = dataArray.getJSONArray(i);
                }
                loadList(validQ, group, childConfig, jsonArray, attrField, attrFieldName, level);
                break;
            case ARRAY:
                if (EmptyUtil.isNotEmpty(data)) {
                    jsonArray = data.getJSONArray(key);
                } else {
                    jsonArray = dataArray.getJSONArray(i);
                }
                loadArray(validQ, group, childConfig, jsonArray, attrField, attrFieldName, level);
                break;
            default:
                break;
        }
    }
}
